<!doctype html>
<html lang="en">
  <body>
    <script src="chrome://__addonRef__/content/lib/js/jquery.min.js"></script>
    <script src="chrome://__addonRef__/content/lib/js/dx.all.js"></script>
    <link
      rel="stylesheet"
      type="text/css"
      href="chrome://zotero-platform/content/zotero.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="chrome://__addonRef__/content/lib/css/dx.light.compact.css"
    />
    <style>
      html,
      body,
      div {
        height: 100%;
        width: 100%;
        padding: 0;
        margin: 0;
        animation: append-animate 0.3s ease-in-out;
      }

      body {
        overflow: hidden;
      }

      @keyframes append-animate {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }

      .form {
        display: flex;
      }

      .form > div {
        display: inline-block;
        vertical-align: top;
      }

      #treeview {
        padding: 8px 0 0 16px;
      }

      .drive-header {
        min-height: auto;
        padding: 0;
        cursor: default;
      }

      .drive-panel {
        font-size: 115%;
        font-weight: bold;
        border-right: 1px solid rgba(165, 165, 165, 0.4);
        height: 100%;
      }

      .drive-panel:last-of-type {
        border-right: none;
      }

      ul,
      li {
        display: list-item;
      }

      #outline-container {
        color: var(--fill-primary);
      }

      .dx-treeview-item {
        border-radius: 5px;
        cursor: auto;
      }

      .dx-scrollable-container {
        padding-right: 8px;
      }

      .dx-treeview-toggle-item-visibility {
        color: var(--fill-primary);
      }

      .dx-treeview-toggle-item-visibility::before {
        content: "";
        position: absolute;
        display: block;
        top: 50%;
        margin-top: -8px;
        left: 50%;
        margin-left: -8px;
        width: 16px;
        height: 16px;
        padding: 4px;
        box-sizing: border-box;
        background: url("chrome://zotero/skin/8/universal/chevron-8.svg")
          content-box no-repeat;
        -moz-context-properties: fill, fill-opacity;
        fill: var(--fill-secondary);
        transition: transform 0.125s ease;
        transform: rotate(-90deg);
      }

      .dx-treeview-toggle-item-visibility.dx-treeview-toggle-item-visibility-opened::before {
        content: "";
        margin-top: -8px;
        margin-left: -8px;
        width: 16px;
        height: 16px;
        transform: rotate(0deg);
      }

      :not(.dx-state-focused) > .dx-treeview-item.dx-state-hover {
        color: var(--fill-primary);
        background-color: transparent;
      }

      .dx-state-focused > .dx-treeview-item {
        background-color: var(--color-accent) !important;
      }
    </style>
    <div
      class="dx-viewport"
      id="outline-container"
      style="background-color: inherit; overflow: hidden"
    >
      <script id="code">
        var cachedNodes = null;
        function init() {
          window.addEventListener("message", handler, false);
          window.postMessage({ type: "ready" }, "*");
          getData();
        }

        function createTreeView(selector, items) {
          $(selector).dxTreeView({
            items,
            expandNodesRecursive: false,
            dataStructure: "plain",
            height: $("window").height(),
            displayExpr: "name",
            noDataText: "Outline is Empty",
            onItemClick: jumpNode,
          });
        }

        function createSortable(selector, driveName) {
          $(selector).dxSortable({
            filter: ".dx-treeview-item",
            group: "shared",
            data: driveName,
            allowDropInsideItem: true,
            allowReordering: true,
            onDragChange: (e) => {
              if (e.fromComponent === e.toComponent) {
                const $nodes = e.element.find(".dx-treeview-node");
                const isDragIntoChild =
                  $nodes.eq(e.fromIndex).find($nodes.eq(e.toIndex)).length > 0;
                if (isDragIntoChild) {
                  e.cancel = true;
                }
              }
            },
            onDragEnd: (e) => {
              if (
                e.fromComponent === e.toComponent &&
                e.fromIndex === e.toIndex
              ) {
                return;
              }
              window._e = e;

              const fromTreeView = getTreeView();
              const toTreeView = getTreeView();

              const fromNode = findNode(fromTreeView, e.fromIndex);
              const toNode = findNode(toTreeView, calculateToIndex(e));

              if (
                e.dropInsideItem &&
                toNode !== null &&
                // !toNode.itemData.isDirectory &&
                toNode.itemData.level === 7
              ) {
                return;
              }

              window.postMessage(
                {
                  type: "moveNode",
                  fromID: parseInt(fromNode.itemData.id),
                  toID: toNode
                    ? parseInt(toNode.itemData.id)
                    : toItems[toItems.length - 1].itemData.id,
                  moveType: e.dropInsideItem ? "child" : "before",
                },
                "*",
              );

              const fromTopVisibleNode = getTopVisibleNode(fromTreeView);
              const toTopVisibleNode = getTopVisibleNode(toTreeView);

              const fromItems = fromTreeView.option("items");
              const toItems = toTreeView.option("items");
              moveNode(fromNode, toNode, fromItems, toItems, e.dropInsideItem);

              fromTreeView.option("items", fromItems);
              toTreeView.option("items", toItems);
              fromTreeView.scrollToItem(fromTopVisibleNode);
              toTreeView.scrollToItem(toTopVisibleNode);
            },
          });
        }

        function getTreeView() {
          try {
            return $("#treeview").dxTreeView("instance");
          } catch {
            return undefined;
          }
        }

        function calculateToIndex(e) {
          if (e.fromComponent !== e.toComponent || e.dropInsideItem) {
            return e.toIndex;
          }

          return e.fromIndex >= e.toIndex ? e.toIndex : e.toIndex + 1;
        }

        function findNode(treeView, index) {
          const nodeElement = treeView.element().find(".dx-treeview-node")[
            index
          ];
          if (nodeElement) {
            return findNodeById(
              treeView.getNodes(),
              nodeElement.getAttribute("data-item-id"),
            );
          }
          return null;
        }

        function findNodeById(nodes, id) {
          for (let i = 0; i < nodes.length; i += 1) {
            if (nodes[i].itemData.id === id) {
              return nodes[i];
            }
            if (nodes[i].children) {
              const node = findNodeById(nodes[i].children, id);
              if (node != null) {
                return node;
              }
            }
          }
          return null;
        }

        function moveNode(
          fromNode,
          toNode,
          fromItems,
          toItems,
          isDropInsideItem,
        ) {
          const fromIndex = findIndex(fromItems, fromNode.itemData.id);
          fromItems.splice(fromIndex, 1);

          const toIndex =
            toNode === null || isDropInsideItem
              ? toItems.length
              : findIndex(toItems, toNode.itemData.id);

          toItems.splice(toIndex, 0, fromNode.itemData);

          moveChildren(fromNode, fromItems, toItems);
          if (isDropInsideItem) {
            fromNode.itemData.parentId = toNode.itemData.id;
          } else {
            fromNode.itemData.parentId =
              toNode != null ? toNode.itemData.parentId : undefined;
          }
        }

        function moveChildren(node, fromItems, toItems) {
          if (!node.itemData.isDirectory) {
            return;
          }

          node.children.forEach((child) => {
            if (child.itemData.isDirectory) {
              moveChildren(child, fromItems, toItems);
            }

            const fromIndex = findIndex(fromItems, child.itemData.id);
            fromItems.splice(fromIndex, 1);
            toItems.splice(toItems.length, 0, child.itemData);
          });
        }

        function findIndex(array, id) {
          const idsArray = array.map((elem) => elem.id);
          return idsArray.indexOf(id);
        }

        function getTopVisibleNode(component) {
          const treeViewElement = component.element().get(0);
          const treeViewTopPosition =
            treeViewElement.getBoundingClientRect().top;
          const nodes = treeViewElement.querySelectorAll(".dx-treeview-node");
          for (let i = 0; i < nodes.length; i += 1) {
            const nodeTopPosition = nodes[i].getBoundingClientRect().top;
            if (nodeTopPosition >= treeViewTopPosition) {
              return nodes[i];
            }
          }

          return null;
        }

        function walkNode(node, callback) {
          if (!node) {
            return;
          }
          callback(node);
          node.children.forEach((child) => walkNode(child, callback));
        }

        function jumpNode(e) {
          var itemData = e.itemData;
          if (itemData.noteLink) {
            window.postMessage(
              {
                type: "openNote",
                link: itemData.noteLink,
                id: parseInt(itemData.id),
              },
              "*",
            );
          } else {
            window.postMessage(
              {
                type: "jumpNode",
                lineIndex: itemData.lineIndex,
                id: parseInt(itemData.id),
              },
              "*",
            );
          }
        }

        const noteIcon = `<svg t="1652008007954" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10521" width="18" height="18"><defs><style>path{fill:currentColor;}</style></defs><path d="M574.3 896H159.7c-17.6 0-31.9-14.3-31.9-32V160c0-17.7 14.3-32 31.9-32h382.7v160c0 35.3 28.6 64 63.8 64h159.5v192c0 17.7 14.3 32 31.9 32 17.6 0 31.9-14.3 31.9-32V270.2c0-8.5-3.3-16.6-9.3-22.6L647.4 73.4c-6-6-14.1-9.4-22.6-9.4h-497C92.6 64 64 92.7 64 128v768c0 35.3 28.6 64 63.8 64h446.5c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM638.1 288c-17.6 0-31.9-14.3-31.9-32V128l159.5 160H638.1z" p-id="10522"></path><path d="M418.8 673H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h193.3c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM608.2 481H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h382.7c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM225.5 353h191.4c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32zM862.7 959.4c-23.6 0-47-8.8-64.8-26.6l-24.4-24.4c-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0l24.4 24.4c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3L749.2 702.6c-11.3-11.4-30.7-10.4-43.2 2.1-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4 12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0c-36.2-36.3-35.2-96.3 2.1-133.8 37.4-37.5 97.2-38.4 133.4-2.1l139.1 139.5c36.2 36.3 35.2 96.3-2.1 133.8-19 19.2-43.9 28.8-68.6 28.8z" p-id="10523"></path><path d="M696.3 883.1c-23.6 0-47-8.8-64.8-26.6l-139-139.6c-17.7-17.8-27.2-41.7-26.6-67.2 0.6-25 10.8-48.6 28.7-66.6 17.9-17.9 41.5-28.2 66.4-28.8 25.5-0.6 49.3 8.9 67 26.6l24.4 24.4c12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0l-24.4-24.4c-5.3-5.3-12.5-8.1-20.4-7.9-8.4 0.2-16.5 3.8-22.8 10-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4L676.7 811c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0c36.2 36.3 35.3 96.3-2.1 133.8-19.1 19.3-44 29-68.7 29z" p-id="10524"></path></svg>`;

        function getData() {
          window.postMessage({ type: "getMindMapData" }, "*");
        }

        function setData(nodes, expandLevel) {
          console.log(nodes);
          const tree = getTreeView();
          const expandedStatus = {};
          if (tree) {
            const rootNodes = tree.getNodes();
            console.log(tree, rootNodes);
            rootNodes.forEach((root) => {
              walkNode(root, (node) => {
                expandedStatus[node.itemData.name] = node.itemData.expanded;
              });
            });
          }
          if (cachedNodes?.length === nodes?.length) {
            let isSame = true;
            for (let i = 0; i < nodes.length; i += 1) {
              const current = nodes[i];
              const cached = cachedNodes[i];
              if (
                current.model.name !== cached.model.name ||
                current.model.level !== cached.model.level ||
                current.model.lineIndex !== cached.model.lineIndex ||
                current.model.endIndex !== cached.model.endIndex
              ) {
                isSame = false;
                break;
              }
            }
            if (isSame) {
              return;
            }
          }
          cachedNodes = nodes;
          console.log(expandedStatus);
          const treeData = [];
          nodes.forEach((node) => {
            treeData.push({
              id: String(node.model.id),
              name: node.model.name,
              level: node.model.level,
              icon: node.model.level === 7 ? noteIcon : undefined,
              lineIndex: node.model.lineIndex,
              endIndex: node.model.endIndex,
              isDirectory: node.children.length > 0,
              expanded:
                node.model.name in expandedStatus
                  ? expandedStatus[node.model.name]
                  : node.model.level < expandLevel,
              parentId:
                node.parent.model.id !== -1
                  ? String(node.parent.model.id)
                  : undefined,
            });
          });
          $(() => {
            createTreeView("#treeview", treeData);
            createSortable("#treeview", "treeData");
          });
        }

        function handler(e) {
          console.log(e);
          if (e.data.type === "setMindMapData") {
            setData(e.data.nodes, e.data.expandLevel);
          }
        }

        window.addEventListener("DOMContentLoaded", () => {
          try {
            init();
          } catch (e) {
            window.postMessage({ type: "error", event: e }, "*");
          }
        });
      </script>
      <div class="demo-container">
        <div class="form">
          <div class="drive-panel">
            <div id="treeview" style="color: currentColor"></div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
